package com.gzhryc.jdbc.utils;

import com.gzhryc.jdbc.annotaion.DateBranchJoin;
import com.gzhryc.jdbc.annotaion.DateBranchTable;
import com.gzhryc.jdbc.annotaion.Join;
import com.gzhryc.jdbc.models.DbColumn;
import com.mysql.jdbc.Blob;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 数据库查询结果集读取工具
 *
 * @author 张浩
 * @version 1.0.0
 */
public class ResultSetUtils {
	
	static Pattern pat = Pattern.compile(".*(_\\d+)$");

    /**
     * 读取一行数据，返回指定的对象
     *
     * @param rs
     * @param clazz
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static <T> T read(ResultSet rs, Class<T> clazz) throws SQLException {
        T result = null;
        if (rs.next()) {
            try {
                Map<String, Field> fields = EntityUtils.findTableColumn2(clazz, true);
                result = clazz.newInstance();
                setFields(rs, result, fields);
            } catch (InstantiationException | IllegalAccessException e) {
                throw new SQLException(e);
            }
        }
        return result;
    }

    public static <T> T readJoin(ResultSet rs, Class<T> clazz) throws SQLException {
        T result = null;
        if (rs.next()) {
            try {
                String tableSuffix = null;
                DateBranchTable dateBranchTable = clazz.getAnnotation(DateBranchTable.class);
                if (dateBranchTable != null) {
                    String tableAlias = rs.getMetaData().getTableName(1);
                    Matcher mat = pat.matcher(tableAlias);
                    if(mat.find()) {
                    	tableSuffix = mat.group(1);
                    }
                }

                Map<String, Field> fields = EntityUtils.findTableColumn2(clazz, true);
                Map<String, Map<String, Field>> joinFields = findJoinFields(clazz, fields, tableSuffix);
                result = clazz.newInstance();
                setFields(rs, result, fields, joinFields);
            } catch (InstantiationException | IllegalAccessException e) {
                throw new SQLException(e);
            }
        }
        return result;
    }

    public static <T> Map<String, Map<String, Field>> findJoinFields(Class<T> clazz, Map<String, Field> fields, String tableSuffix) {
        Map<String, Map<String, Field>> joinFields = new HashMap<String, Map<String, Field>>();
        for (Field field : fields.values()) {
            Join join = field.getAnnotation(Join.class);
            if (join != null) {
                Class<?> typeClass = field.getType();
                String tableName = join.toTable();
                DateBranchJoin dateBranchJoin = field.getAnnotation(DateBranchJoin.class);
                if (dateBranchJoin != null && tableSuffix != null) {
                    if (StringUtils.isBlank(tableName)) {
                        tableName = EntityUtils.getTableName(typeClass);
                    }
                    tableName = tableName + tableSuffix;
                }else if(StringUtils.isBlank(tableName)){
                    tableName = EntityUtils.getTableName(typeClass);
                }
                if(StringUtils.isBlank(tableName)){
                    tableName = field.getName();
                }
                if(!joinFields.keySet().contains(tableName)) {
                    Map<String, Field> fieldItems = EntityUtils.findTableColumn2(typeClass, true);
                    joinFields.put(tableName, fieldItems);
                }
            }
        }
        return joinFields;
    }

    public static <T> void setFields(ResultSet rs, T entity, Map<String, Field> fields, Map<String, Map<String, Field>> joinFields) throws SQLException {
        com.mysql.jdbc.Field[] ddd = null;
        Object ccc = ClassTools.getFieldValue(rs.getMetaData(),"fields");
        if(ccc != null){
            ddd = (com.mysql.jdbc.Field[])ccc;
        }
        for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
            String label = rs.getMetaData().getColumnLabel(i);
            String alias = ddd[(i-1)].getTableName();
            try {
                Object obj = rs.getObject(i);
                if (obj != null) {
                    //使用别名
                    Field field = fields.get(alias.toUpperCase());
                    if(field == null) {
                        field = fields.get(label.toUpperCase());
                    }
                    if(field != null) {
                        Object entityObj = entity;
                        //在对象里Join自己时，需要判断是否是基础类型
                        if(!ClassTools.isBaseType(field.getType())) {
                            if(!Date.class.equals(field.getType())) {
                                String tableName = ddd[(i - 1)].getOriginalTableName();
                                Map<String, Field> itemFields = joinFields.get(tableName);
                                if (itemFields == null) {
                                    itemFields = joinFields.get(alias);
                                }
                                if (itemFields != null) {
                                    Field fieldItem = itemFields.get(label.toUpperCase());
                                    if (fieldItem != null) {
                                        entityObj = field.get(entity);
                                        if (entityObj == null) {
                                            entityObj = field.getType().newInstance();
                                            ClassTools.setFieldValue(entity, field, entityObj);
                                        }
                                        field = fieldItem;
                                    }
                                }
                            }
                        }

                        Date date = null;

                        if (obj.getClass().equals(java.sql.Date.class)) {
                            date = new Date(((java.sql.Date) obj).getTime());
                        } else if (obj.getClass().equals(Timestamp.class)) {
                            date = new Date(((Timestamp) obj).getTime());
                        } else if (obj.getClass().equals(Time.class)) {
                            date = new Date(((Time) obj).getTime());
                        }

                        if (field.getType().equals(Date.class)) {
                            field.set(entityObj, date);
                        } else if (date != null) {
                            if (field.getType().equals(Long.class) || field.getType().equals(long.class)) {
                                field.set(entityObj, date.getTime());
                            } else if (field.getType().equals(String.class)) {
                                if (obj.getClass().equals(java.sql.Date.class)) {
                                    field.set(entityObj, DateFormatUtils.format(date, "yyyy-MM-dd"));
                                } else if (obj.getClass().equals(Timestamp.class)) {
                                    field.set(entityObj, DateFormatUtils.format(date, "yyyy-MM-dd HH:mm:ss"));
                                } else {
                                    field.set(entityObj, DateFormatUtils.format(date, "HH:mm:ss"));
                                }
                            }
                        } else {
                            ClassTools.setFieldValue(entityObj, field, obj);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static <T> void setFields(ResultSet rs, T entity, Map<String, Field> fields) throws SQLException {
        for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
            String label = rs.getMetaData().getColumnLabel(i);
            try {
                Object obj = rs.getObject(i);
                if (obj != null) {
                    Field field = fields.get(label.toUpperCase());

                    if (field != null) {
                        Date date = null;
                        if (obj.getClass().equals(java.sql.Date.class)) {
                            date = new Date(((java.sql.Date) obj).getTime());
                        } else if (obj.getClass().equals(Timestamp.class)) {
                            date = new Date(((Timestamp) obj).getTime());
                        } else if (obj.getClass().equals(Time.class)) {
                            date = new Date(((Time) obj).getTime());
                        } else if (obj.getClass().equals(Blob.class)) {
                            Blob blob = (Blob) obj;
                            field.set(obj, blob.getBytes(0, (int) blob.length()));
                            continue;
                        }

                        if (field.getType().equals(Date.class)) {
                            field.set(entity, date);
                        } else if (date != null) {
                            if (field.getType().equals(Long.class) || field.getType().equals(long.class)) {
                                field.set(entity, date.getTime());
                            } else if (field.getType().equals(String.class)) {
                                if (obj.getClass().equals(java.sql.Date.class)) {
                                    field.set(entity, DateFormatUtils.format(date, "yyyy-MM-dd"));
                                } else if (obj.getClass().equals(Timestamp.class)) {
                                    field.set(entity, DateFormatUtils.format(date, "yyyy-MM-dd HH:mm:ss"));
                                } else {
                                    field.set(entity, DateFormatUtils.format(date, "HH:mm:ss"));
                                }
                            }
                        } else {
                            ClassTools.setFieldValue(entity, field, obj);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 读取多行数据
     *
     * @param rs
     * @param clazz
     * @return
     * @throws SQLException
     */
    public static <T> List<T> readList(ResultSet rs, Class<T> clazz) throws SQLException {
        List<T> result = null;
        Map<String, Field> fields = EntityUtils.findTableColumn2(clazz, true);
        if (rs.next()) {
            result = new ArrayList<T>();
            if(ClassTools.isBaseType(clazz)){
                do {
                    Object obj = rs.getObject(1);
                    if(obj != null) {
                        T item = ClassTools.toType(clazz, obj);
                        result.add(item);
                    }
                } while (rs.next());
            }else {
                do {
                    try {
                        T item = clazz.newInstance();
                        setFields(rs, item, fields);
                        result.add(item);
                    } catch (InstantiationException | IllegalAccessException e) {
                        e.printStackTrace();
                    }
                } while (rs.next());
            }
        }
        return result;
    }

    public static <T> List<T> readJoinList(ResultSet rs, Class<T> clazz) throws SQLException {
        List<T> result = null;
        Map<String, Field> fields = EntityUtils.findTableColumn2(clazz, true);
        if (rs.next()) {
            result = new ArrayList<T>();
            String tableSuffix = null;
            DateBranchTable dateBranchTable = clazz.getAnnotation(DateBranchTable.class);
            if (dateBranchTable != null) {
                String tableAlias = rs.getMetaData().getTableName(1);
                Matcher mat = pat.matcher(tableAlias);
                if(mat.find()) {
                	tableSuffix = mat.group(1);
                }
            }
            Map<String, Map<String, Field>> joinFields = findJoinFields(clazz, fields, tableSuffix);
            do {
                try {
                    T item = clazz.newInstance();
                    setFields(rs, item, fields, joinFields);
                    result.add(item);
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
            } while (rs.next());
        }
        return result;
    }

    /**
     * 获取结果表的字段集
     *
     * @param rs
     * @return
     * @throws SQLException
     */
    public static List<DbColumn> getColumns(ResultSet rs) throws SQLException {
        List<DbColumn> result = new ArrayList<DbColumn>();
        for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
            DbColumn col = new DbColumn();
            col.setName(rs.getMetaData().getColumnLabel(i));
            col.setType(rs.getMetaData().getColumnTypeName(i));
            result.add(col);
        }
        return result;
    }

    /**
     * 获取结果表的字段名称
     *
     * @param rs
     * @return
     * @throws SQLException
     */
    public static List<String> getColumnLables(ResultSet rs) throws SQLException {
        List<String> result = new ArrayList<String>();
        for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
            result.add(rs.getMetaData().getColumnLabel(i));
        }
        return result;
    }

    /**
     * 读取一行数据，返回一个Map对象
     *
     * @param rs
     * @return
     * @throws SQLException
     */
    public static Map<String, Object> read(ResultSet rs) throws SQLException {
        Map<String, Object> result = null;
        if (rs.next()) {
            result = new LinkedHashMap<String, Object>();
            for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
                String name = rs.getMetaData().getColumnLabel(i);
                try {
                    Object obj = rs.getObject(name);
                    if (obj != null && obj.getClass().equals(java.sql.Date.class)) {
                        result.put(name, new Date(((java.sql.Date) obj).getTime()));
                    } else if (obj != null && obj.getClass().equals(Timestamp.class)) {
                        result.put(name, new Date(((Timestamp) obj).getTime()));
                    } else if (obj != null && obj.getClass().equals(Time.class)) {
                        result.put(name, new Date(((Time) obj).getTime()));
                    } else {
                        result.put(name, rs.getObject(name));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }

    /**
     * 读取多行数据，返回多个Map对象
     *
     * @param rs
     * @return
     * @throws SQLException
     */
    public static List<Map<String, Object>> readList(ResultSet rs) throws SQLException {
        List<Map<String, Object>> result = null;
        Map<String, Object> item = read(rs);
        if (item != null) {
            result = new ArrayList<Map<String, Object>>();
            do {
                result.add(item);
            } while ((item = read(rs)) != null);
        }
        return result;
    }

}
